import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.*;
import java.lang.Math;


class Nomnom extends JPanel implements KeyListener
{
	java.util.List<Collidable> food = new ArrayList<Collidable>();
	
	boolean up, down, left, right;
	boolean alive = true;
	int numberOfFish = 0;
	
	Player a = new Player();
	
	volatile Thread gameThread;
	volatile Thread spawner;
	
	public static void main(String[] args) {
	
		JFrame frame = new JFrame("Nomnom");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		Nomnom n = new Nomnom();
		
		frame.add(n);
		frame.addKeyListener(n); 
		frame.setSize(775,750);
		frame.setVisible(true);
	}
	
	public Nomnom()
	{
		food.add(a);
		gameThread = new GameThread(this);
		spawner = new FishSpawner(this);
		gameThread.start();
		spawner.start();
	}
	
	
	static class GameThread extends Thread
	{
		Nomnom n;
		
		GameThread(Nomnom n)
		{
			this.n = n;
		}
		public void run()
		{
			n.gO();
		}
	}
	static class FishSpawner extends Thread
	{
		Nomnom n;
		
		FishSpawner(Nomnom n)
		{
			this.n = n;
		}
		public void run()
		{
			n.spawn();
		}
	}
	public void gO()
	{
		
		while (alive && Thread.currentThread()==gameThread)
		{
			for(Collidable e: new ArrayList<Collidable>(food))	e.step();

			
			ArrayList<Collidable> copy = new ArrayList<Collidable>(food);
			for(int i=1; i<copy.size(); i++) {
				Collidable a = copy.get(0);
				Collidable b = copy.get(i);
				if(a.boundingBox().intersects(b.boundingBox())) collision(a,b);
				
			}
			

			repaint();
			
			
			try { Thread.sleep(1000/60); } catch (Exception e) {};
		}
		System.out.println("Game over");
	}	
	public void collision(Collidable a, Collidable b)
	{
		
		
		if(a.size <= b.size)
		{
			food.remove(a);
			if( a instanceof Player ) alive = false;
			//loseSplashScreen();
		}
		else
		{
			food.remove(b);
			if (b instanceof Food )numberOfFish--;
			a.size += (Math.log(Math.log(b.size + 1) + 1 ))/ 25;
		}
	}
	public void spawnVirus()
	{
		food.add( new Virus() );
	}
	public void removeViri()
	{
		for ( int i=0; i < food.size(); i++) {
			if (food.get(i) instanceof Virus) {
				food.remove(i);
			}
		}
	}
	public void spawn()
	{				
		while (alive && Thread.currentThread()==spawner)
		{			
			if ( numberOfFish < 8)
			{
				addFish();
			}
			try{ Thread.sleep( (int)(10000/randomBetween(30,200) ));} catch (Exception e) {};
		}
	}
	
	public void paint(Graphics g1)
	{	
		Graphics2D g = (Graphics2D) g1;
		if(!alive)
		{
			for (int i = 0; i < food.size(); i++) {
				food.remove(i);
			}
			g.drawImage(imageFromFile("background.PNG"),0,0,null);
			g.drawString("YOU LOSE", 350,350);
			g.drawString("PRESS SPACE TO PLAY AGAIN", 350,450);
		}
		else if(alive) {
			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g.drawImage(imageFromFile("background.PNG"),0,0,null);
			int score = (int)((a.size * 10000)-9999);
			g.drawString(""+score, 10,10);
			//g.setColor(Color.BLUE);
			//g.fillRect(0,0,2000,2000);
			for( Collidable f: new ArrayList<Collidable>(food)) f.paint(g);
		}
	}
	public int getNumberOfFish()
	{
		return food.size();
	}
	public void addFish()
	{
		int spawnPosOrNeg = -1;
		if ( Math.random() * 2 > 1) spawnPosOrNeg = 1;

		double newspeed = ( randomBetween(1,4) * 1 * spawnPosOrNeg);
		double newsize = ( randomBetween(.005,.15) );
		food.add(  new Food( newsize, newspeed) );
		numberOfFish++;
	}
	public double randomBetween(double x, double y)
	{
		return  ((Math.random() * ( y - x)) + x);
	}
	abstract class Collidable
	{
		double size;
		double x,y,vx,vy;
		double intersectionWidth;
		double intersectionHeight;
		
		abstract void step();
		public void paint(Graphics g)
		{	
			//if( this instanceof Virus ) return;
			//g.setColor(Color.GREEN);
			//g.drawRect( (int)(x - intersectionWidth/2.0*Math.sqrt(size) ), (int)(y - intersectionHeight/2.0*Math.sqrt(size)),
			//	(int)(intersectionWidth* Math.sqrt(size) ), (int)(intersectionHeight*Math.sqrt(size) ) ); 
		}
		public Rectangle boundingBox()
		{
			return new Rectangle(  (int)(x - intersectionWidth/2.0*Math.sqrt(size) ), (int)(y - intersectionHeight/2.0*Math.sqrt(size)),
				(int)(intersectionWidth* Math.sqrt(size) ), (int)(intersectionHeight*Math.sqrt(size) ) );
		}
	}
	class Player extends Collidable
	{
		int direction = 1;
			
		
		Image image = imageFromFile("player2.PNG");
		int height = image.getHeight(null); 
		int width = image.getWidth(null);
		public Player()
		{
			size = .01; x = 350; y = 350; 
			intersectionWidth = ( width - (0.2 * width) );
			intersectionHeight = (height - (0.2 * height) );
		}
		public void paint(Graphics g1)
		{
			Graphics2D g = (Graphics2D) g1;
			AffineTransform trans = new AffineTransform();
			trans.translate(x,y);
			trans.scale(Math.sqrt(size) * 1.0 * direction,Math.sqrt(size) * 1.0);
			trans.translate(-width/2,-height/2);
			g.drawImage(image, trans, null);	
			
			super.paint(g1);
		}
		public void step()
		{
			//player movement
			if (up) vy = vy - .1;
			if (down) vy = vy + .1;
			if (right) {
				direction = 1;
				vx = vx + .1;
			}
			if (left) {
				direction = -1;
				vx = vx - .1;
			}
			
			//friction
			if(vx > 0)
			{
				if (vx < .10)
				{
					vx = 0;
				} 
				else 
				{
					vx -=.05;
				}
			}
			else if (vx < 0) 
			{
				if (vx > -.10)
				{
					vx = 0;
				}
				else 
				{
					vx +=  .05;
				}
			}
			if(vy > 0)
			{
				if (vy < .10)
				{
					vy = 0;
				} 
				else 
				{
					vy -=.05;
				}
			}
			else if (vy < 0) 
			{
				if (vy > -.10)
				{
					vy = 0;
				}
				else 
				{
					vy +=  .05;
				}
			}
			
			//moving the character
			x += vx;
			y += vy;
			
			//if hit edge of map or walls
			if (x < 0) {x = 690; }
			if (x > 700) {x = 10;}
			if (y <= 10) {y = 11; vy = -.01;}
			if (y >= 640 + Math.sqrt(size)/2) {y = (639 - Math.sqrt(size)/2); vy = +.01;}
		}		
	}	
	class Food extends Collidable
	{
		double velocity = 0;
		double direction = 1;
		
		Image imageFood = imageFromFile("food2.PNG");
		int height = imageFood.getHeight(null); 
		int width = imageFood.getWidth(null);
	
		public Food( double s, double v)
		{
			
			intersectionWidth = ( width - (0.2 * width) );
			intersectionHeight = (height - (0.2 * height) );
		
			velocity = v;
			size = s;
			
			if (velocity > 0) { x = -50; y = Math.random() * 650; direction = -1;}
			else if (velocity < 0) { x = 750; y = Math.random() * 650; direction = 1; }
		}
		public void paint(Graphics g1)
		{
			Graphics2D r = (Graphics2D) g1;
			AffineTransform trans = new AffineTransform();
			trans.translate(x,y);
			trans.scale(Math.sqrt(size) * 1.0 * direction,Math.sqrt(size) * 1.0);
			trans.translate(-width/2,-height/2);
			
			r.drawImage(imageFood, trans, null);
			
			super.paint(g1);
		}		
		public void step()
		{
			x += velocity;
			if (x < -100) {food.remove(this); numberOfFish--;}
			if (x > 800) {food.remove(this); numberOfFish--;}
		}
	}
	
	class Virus extends Collidable
	{
		Image i = imageFromFile("rhinovirus.PNG");
		int height = i.getHeight(null); 
		int width = i.getWidth(null);
		int ttl = 100 + (int)(Math.random()*10);
		
		public Virus()
		{
			x=800*Math.random();
			y=600*Math.random();
			vx=Math.random()-0.5;
			vy=Math.random()-0.5;
			size=0.005;
		
			intersectionWidth = width;
			intersectionHeight = height;
		}		
		public void paint(Graphics gOld)
		{
			Graphics2D g = (Graphics2D) gOld;
			
			AffineTransform trans = new AffineTransform();
			trans.translate(x,y);
			trans.scale(.1,.1);
			trans.translate(-width/2,-height/2);
		
			g.drawImage(i, trans, null);
			
			super.paint(gOld);
		}
		public void step()
		{
			ttl--;
			if(ttl<0) {
				food.remove(this);
				
				if( food.size() < 1000 || Math.random() < 0.1){
				
					Virus a = new Virus();
					Virus b = new Virus();
					b.x = x + Math.random()*10;
					a.x = x + Math.random()*10;
					b.y = y + Math.random()*10;
					a.y = y + Math.random()*10;
					food.add(a);
					food.add(b);
				}
			}
			x+=vx; y+=vy;
			if (x < -20) {x = 820; }
			if (x > 820) {x = -20;}
			if (y <= 0) food.remove(this);
			if (y > 640) food.remove(this);
		}
	}

	public void keyTyped(KeyEvent e)
	{
	}
	public void keyReleased(KeyEvent e)
	{
		if(e.getKeyCode()==KeyEvent.VK_UP) up=false;
		if(e.getKeyCode()==KeyEvent.VK_DOWN) down=false;
		if(e.getKeyCode()==KeyEvent.VK_LEFT) left=false;
		if(e.getKeyCode()==KeyEvent.VK_RIGHT) right=false;
		
	}
	public void keyPressed(KeyEvent e)
	{
		if(e.getKeyCode()==KeyEvent.VK_UP) up=true;
		if(e.getKeyCode()==KeyEvent.VK_DOWN) down=true;
		if(e.getKeyCode()==KeyEvent.VK_LEFT) left=true;
		if(e.getKeyCode()==KeyEvent.VK_RIGHT) right=true;
		if(e.getKeyCode()==KeyEvent.VK_V) spawnVirus();
		if(e.getKeyCode()==KeyEvent.VK_R) removeViri();
		if(e.getKeyCode()==KeyEvent.VK_SPACE) { 
			
			alive = true; 
			numberOfFish = 0;
			Player a = new Player(); 
			food.clear();
			food.add(a);
			
			gameThread = new GameThread(this);
			spawner = new FishSpawner(this);
			gameThread.start();
			spawner.start();
		}
	}
	public static Image imageFromFile(String filename)
	{
		ImageIcon i = new ImageIcon(filename);
		if (i.getIconHeight() == -1 ) throw new RuntimeException( "Can not load image! >:O " + filename );
		return i.getImage();
	}

}